// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
 *******************************************************************************
 * Copyright (C) 2001-2010, International Business Machines Corporation and    *
 * others. All Rights Reserved.                                                *
 *******************************************************************************
 */
package com.ibm.icu.dev.test.iterator;

import com.ibm.icu.dev.test.CoreTestFmwk;
import com.ibm.icu.text.ReplaceableString;
import com.ibm.icu.text.UCharacterIterator;
import com.ibm.icu.text.UTF16;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

/**
 * @author ram
 *     <p>To change this generated comment edit the template variable "typecomment":
 *     Window>Preferences>Java>Templates. To enable and disable the creation of type comments go to
 *     Window>Preferences>Java>Code Generation.
 */
@RunWith(JUnit4.class)
public class TestUCharacterIterator extends CoreTestFmwk {

    // constructor -----------------------------------------------------

    /** Constructor */
    public TestUCharacterIterator() {}

    // public methods --------------------------------------------------

    /** Testing cloning */
    @Test
    public void TestClone() throws CloneNotSupportedException {
        UCharacterIterator iterator = UCharacterIterator.getInstance("testing");
        UCharacterIterator cloned = iterator.clone();
        int completed = 0;
        while (completed != UCharacterIterator.DONE) {
            completed = iterator.next();
            if (completed != cloned.next()) {
                errln("Cloned operation failed");
            }
        }
    }

    public void getText(UCharacterIterator iterator, String result) {
        /* test getText */
        char[] buf = new char[1];
        for (; ; ) {
            try {
                iterator.getText(buf);
                break;
            } catch (IndexOutOfBoundsException e) {
                buf = new char[iterator.getLength()];
            }
        }
        if (result.compareTo(new String(buf, 0, iterator.getLength())) != 0) {
            errln("getText failed for iterator");
        }
    }

    /** Testing iteration */
    @Test
    public void TestIteration() {
        UCharacterIterator iterator = UCharacterIterator.getInstance(ITERATION_STRING_);
        UCharacterIterator iterator2 = UCharacterIterator.getInstance(ITERATION_STRING_);
        iterator.setToStart();
        if (iterator.current() != ITERATION_STRING_.charAt(0)) {
            errln("Iterator failed retrieving first character");
        }
        iterator.setToLimit();
        if (iterator.previous() != ITERATION_STRING_.charAt(ITERATION_STRING_.length() - 1)) {
            errln("Iterator failed retrieving last character");
        }
        if (iterator.getLength() != ITERATION_STRING_.length()) {
            errln("Iterator failed determining begin and end index");
        }
        iterator2.setIndex(0);
        iterator.setIndex(0);
        int ch = 0;
        while (ch != UCharacterIterator.DONE) {
            int index = iterator2.getIndex();
            ch = iterator2.nextCodePoint();
            if (index != ITERATION_SUPPLEMENTARY_INDEX) {
                if (ch != iterator.next() && ch != UCharacterIterator.DONE) {
                    errln("Error mismatch in next() and nextCodePoint()");
                }
            } else {
                if (UTF16.getLeadSurrogate(ch) != iterator.next()
                        || UTF16.getTrailSurrogate(ch) != iterator.next()) {
                    errln(
                            "Error mismatch in next and nextCodePoint for "
                                    + "supplementary characters");
                }
            }
        }
        iterator.setIndex(ITERATION_STRING_.length());
        iterator2.setIndex(ITERATION_STRING_.length());
        while (ch != UCharacterIterator.DONE) {
            int index = iterator2.getIndex();
            ch = iterator2.previousCodePoint();
            if (index != ITERATION_SUPPLEMENTARY_INDEX) {
                if (ch != iterator.previous() && ch != UCharacterIterator.DONE) {
                    errln("Error mismatch in previous() and " + "previousCodePoint()");
                }
            } else {
                if (UTF16.getLeadSurrogate(ch) != iterator.previous()
                        || UTF16.getTrailSurrogate(ch) != iterator.previous()) {
                    errln(
                            "Error mismatch in previous and "
                                    + "previousCodePoint for supplementary characters");
                }
            }
        }
    }

    // Tests for new API for utf-16 support
    @Test
    public void TestIterationUChar32() {
        String text = "\u0061\u0062\ud841\udc02\u20ac\ud7ff\ud842\udc06\ud801\udc00\u0061";
        int c;
        int i;
        {
            UCharacterIterator iter = UCharacterIterator.getInstance(text);

            String iterText = iter.getText();
            if (!iterText.equals(text)) errln("iter.getText() failed");

            iter.setIndex(1);
            if (iter.currentCodePoint() != UTF16.charAt(text, 1))
                errln("Iterator didn't start out in the right place.");

            iter.setToStart();
            c = iter.currentCodePoint();
            i = 0;
            i = iter.moveCodePointIndex(1);
            c = iter.currentCodePoint();
            if (c != UTF16.charAt(text, 1) || i != 1)
                errln(
                        "moveCodePointIndex(1) didn't work correctly expected "
                                + hex(c)
                                + " got "
                                + hex(UTF16.charAt(text, 1))
                                + " i= "
                                + i);

            i = iter.moveCodePointIndex(2);
            c = iter.currentCodePoint();
            if (c != UTF16.charAt(text, 4) || i != 4)
                errln(
                        "moveCodePointIndex(2) didn't work correctly expected "
                                + hex(c)
                                + " got "
                                + hex(UTF16.charAt(text, 4))
                                + " i= "
                                + i);

            i = iter.moveCodePointIndex(-2);
            c = iter.currentCodePoint();
            if (c != UTF16.charAt(text, 1) || i != 1)
                errln(
                        "moveCodePointIndex(-2) didn't work correctly expected "
                                + hex(c)
                                + " got "
                                + hex(UTF16.charAt(text, 1))
                                + " i= "
                                + i);

            iter.setToLimit();
            i = iter.moveCodePointIndex(-2);
            c = iter.currentCodePoint();
            if (c != UTF16.charAt(text, (text.length() - 3)) || i != (text.length() - 3))
                errln(
                        "moveCodePointIndex(-2) didn't work correctly expected "
                                + hex(c)
                                + " got "
                                + hex(UTF16.charAt(text, (text.length() - 3)))
                                + " i= "
                                + i);

            iter.setToStart();
            c = iter.currentCodePoint();
            i = 0;

            // testing first32PostInc, nextCodePointPostInc, setTostart
            i = 0;
            iter.setToStart();
            c = iter.next();
            if (c != UTF16.charAt(text, i))
                errln(
                        "first32PostInc failed.  Expected->"
                                + hex(UTF16.charAt(text, i))
                                + " Got-> "
                                + hex(c));
            if (iter.getIndex() != UTF16.getCharCount(c) + i)
                errln("getIndex() after first32PostInc() failed");

            iter.setToStart();
            i = 0;
            if (iter.getIndex() != 0) errln("setToStart failed");

            logln("Testing forward iteration...");
            do {
                if (c != UCharacterIterator.DONE) c = iter.nextCodePoint();

                if (c != UTF16.charAt(text, i))
                    errln(
                            "Character mismatch at position "
                                    + i
                                    + ", iterator has "
                                    + hex(c)
                                    + ", string has "
                                    + hex(UTF16.charAt(text, i)));

                i += UTF16.getCharCount(c);
                if (iter.getIndex() != i)
                    errln("getIndex() aftr nextCodePointPostInc() isn't working right");
                c = iter.currentCodePoint();
                if (c != UCharacterIterator.DONE && c != UTF16.charAt(text, i))
                    errln("current() after nextCodePointPostInc() isn't working right");

            } while (c != UCharacterIterator.DONE);
            c = iter.nextCodePoint();
            if (c != UCharacterIterator.DONE)
                errln("nextCodePointPostInc() didn't return DONE at the beginning");
        }
    }

    class UCharIterator {

        public UCharIterator(int[] src, int len, int index) {

            s = src;
            length = len;
            i = index;
        }

        public int current() {
            if (i < length) {
                return s[i];
            } else {
                return -1;
            }
        }

        public int next() {
            if (i < length) {
                return s[i++];
            } else {
                return -1;
            }
        }

        public int previous() {
            if (i > 0) {
                return s[--i];
            } else {
                return -1;
            }
        }

        public int getIndex() {
            return i;
        }

        private int[] s;
        private int length, i;
    }

    @Test
    public void TestPreviousNext() {
        // src and expect strings
        char src[] = {
            UTF16.getLeadSurrogate(0x2f999),
            UTF16.getTrailSurrogate(0x2f999),
            UTF16.getLeadSurrogate(0x1d15f),
            UTF16.getTrailSurrogate(0x1d15f),
            0xc4,
            0x1ed0
        };
        // iterators
        UCharacterIterator iter1 =
                UCharacterIterator.getInstance(new ReplaceableString(new String(src)));
        UCharacterIterator iter2 = UCharacterIterator.getInstance(src /*char array*/);
        UCharacterIterator iter3 =
                UCharacterIterator.getInstance(new StringCharacterIterator(new String(src)));
        UCharacterIterator iter4 =
                UCharacterIterator.getInstance(new StringBuffer(new String(src)));
        previousNext(iter1);
        previousNext(iter2);
        previousNext(iter3);
        previousNext(iter4);
        getText(iter1, new String(src));
        getText(iter2, new String(src));
        getText(iter3, new String(src));
        /* getCharacterIterator */
        CharacterIterator citer1 = iter1.getCharacterIterator();
        CharacterIterator citer2 = iter2.getCharacterIterator();
        CharacterIterator citer3 = iter3.getCharacterIterator();
        if (citer1.first() != iter1.current()) {
            errln("getCharacterIterator for iter1 failed");
        }
        if (citer2.first() != iter2.current()) {
            errln("getCharacterIterator for iter2 failed");
        }
        if (citer3.first() != iter3.current()) {
            errln("getCharacterIterator for iter3 failed");
        }
        /* Test clone()  && moveIndex()*/
        try {
            UCharacterIterator clone1 = iter1.clone();
            UCharacterIterator clone2 = iter2.clone();
            UCharacterIterator clone3 = iter3.clone();
            if (clone1.moveIndex(3) != iter1.moveIndex(3)) {
                errln("moveIndex for iter1 failed");
            }
            if (clone2.moveIndex(3) != iter2.moveIndex(3)) {
                errln("moveIndex for iter2 failed");
            }
            if (clone3.moveIndex(3) != iter3.moveIndex(3)) {
                errln("moveIndex for iter1 failed");
            }
        } catch (Exception e) {
            errln("could not clone the iterator");
        }
    }

    public void previousNext(UCharacterIterator iter) {

        int expect[] = {0x2f999, 0x1d15f, 0xc4, 0x1ed0};

        // expected src indexes corresponding to expect indexes
        int expectIndex[] = {
            0, 0, 1, 1, 2, 3, 4 // needed
        };

        // initial indexes into the src and expect strings

        final int SRC_MIDDLE = 4;
        final int EXPECT_MIDDLE = 2;

        // movement vector
        // - for previous(), 0 for current(), + for next()
        // not const so that we can terminate it below for the error message
        String moves = "0+0+0--0-0-+++0--+++++++0--------";

        UCharIterator iter32 = new UCharIterator(expect, expect.length, EXPECT_MIDDLE);

        int c1, c2;
        char m;

        // initially set the indexes into the middle of the strings
        iter.setIndex(SRC_MIDDLE);

        // move around and compare the iteration code points with
        // the expected ones
        int movesIndex = 0;
        while (movesIndex < moves.length()) {
            m = moves.charAt(movesIndex++);
            if (m == '-') {
                c1 = iter.previousCodePoint();
                c2 = iter32.previous();
            } else if (m == '0') {
                c1 = iter.currentCodePoint();
                c2 = iter32.current();
            } else { // m=='+'
                c1 = iter.nextCodePoint();
                c2 = iter32.next();
            }

            // compare results
            if (c1 != c2) {
                // copy the moves until the current (m) move, and terminate
                String history = moves.substring(0, movesIndex);
                errln(
                        "error: mismatch in Normalizer iteration at "
                                + history
                                + ": "
                                + "got c1= "
                                + hex(c1)
                                + " != expected c2= "
                                + hex(c2));
                break;
            }

            // compare indexes
            if (expectIndex[iter.getIndex()] != iter32.getIndex()) {
                // copy the moves until the current (m) move, and terminate
                String history = moves.substring(0, movesIndex);
                errln(
                        "error: index mismatch in Normalizer iteration at "
                                + history
                                + " : "
                                + "Normalizer index "
                                + iter.getIndex()
                                + " expected "
                                + expectIndex[iter32.getIndex()]);
                break;
            }
        }
    }

    @Test
    public void TestUCharacterIteratorWrapper() {
        String source = "asdfasdfjoiuyoiuy2341235679886765";
        UCharacterIterator it = UCharacterIterator.getInstance(source);
        CharacterIterator wrap_ci = it.getCharacterIterator();
        CharacterIterator ci = new StringCharacterIterator(source);
        wrap_ci.setIndex(10);
        ci.setIndex(10);
        String moves = "0+0+0--0-0-+++0--+++++++0--------++++0000----0-";
        int c1, c2;
        char m;
        int movesIndex = 0;

        while (movesIndex < moves.length()) {
            m = moves.charAt(movesIndex++);
            if (m == '-') {
                c1 = wrap_ci.previous();
                c2 = ci.previous();
            } else if (m == '0') {
                c1 = wrap_ci.current();
                c2 = ci.current();
            } else { // m=='+'
                c1 = wrap_ci.next();
                c2 = ci.next();
            }

            // compare results
            if (c1 != c2) {
                // copy the moves until the current (m) move, and terminate
                String history = moves.substring(0, movesIndex);
                errln(
                        "error: mismatch in Normalizer iteration at "
                                + history
                                + ": "
                                + "got c1= "
                                + hex(c1)
                                + " != expected c2= "
                                + hex(c2));
                break;
            }

            // compare indexes
            if (wrap_ci.getIndex() != ci.getIndex()) {
                // copy the moves until the current (m) move, and terminate
                String history = moves.substring(0, movesIndex);
                errln(
                        "error: index mismatch in Normalizer iteration at "
                                + history
                                + " : "
                                + "Normalizer index "
                                + wrap_ci.getIndex()
                                + " expected "
                                + ci.getIndex());
                break;
            }
        }
        if (ci.first() != wrap_ci.first()) {
            errln(
                    "CharacterIteratorWrapper.first() failed. expected: "
                            + ci.first()
                            + " got: "
                            + wrap_ci.first());
        }
        if (ci.last() != wrap_ci.last()) {
            errln(
                    "CharacterIteratorWrapper.last() failed expected: "
                            + ci.last()
                            + " got: "
                            + wrap_ci.last());
        }
        if (ci.getBeginIndex() != wrap_ci.getBeginIndex()) {
            errln(
                    "CharacterIteratorWrapper.getBeginIndex() failed expected: "
                            + ci.getBeginIndex()
                            + " got: "
                            + wrap_ci.getBeginIndex());
        }
        if (ci.getEndIndex() != wrap_ci.getEndIndex()) {
            errln(
                    "CharacterIteratorWrapper.getEndIndex() failed expected: "
                            + ci.getEndIndex()
                            + " got: "
                            + wrap_ci.getEndIndex());
        }
        try {
            CharacterIterator cloneWCI = (CharacterIterator) wrap_ci.clone();
            if (wrap_ci.getIndex() != cloneWCI.getIndex()) {
                errln(
                        "CharacterIteratorWrapper.clone() failed expected: "
                                + wrap_ci.getIndex()
                                + " got: "
                                + cloneWCI.getIndex());
            }
        } catch (Exception e) {
            errln("CharacterIterator.clone() failed");
        }
    }

    // private data members ---------------------------------------------

    private static final String ITERATION_STRING_ = "Testing 1 2 3 \ud800\udc00 456";
    private static final int ITERATION_SUPPLEMENTARY_INDEX = 14;

    @Test
    public void TestJitterbug1952() {
        // test previous code point
        char[] src = new char[] {'\uDC00', '\uD800', '\uDC01', '\uD802', '\uDC02', '\uDC03'};
        UCharacterIterator iter = UCharacterIterator.getInstance(src);
        iter.setIndex(1);
        int ch;
        // this should never go into a infinite loop
        // if it does then we have a problem
        while ((ch = iter.previousCodePoint()) != UCharacterIterator.DONE) {
            if (ch != 0xDc00) {
                errln("iter.previousCodePoint() failed");
            }
        }
        iter.setIndex(5);
        while ((ch = iter.nextCodePoint()) != UCharacterIterator.DONE) {
            if (ch != 0xDC03) {
                errln("iter.nextCodePoint() failed");
            }
        }
    }
}
